<?php

	namespace org\tekuna\demo\weblog\manager;
	
	use \PDOStatement;
	use \PDOException;
	
	use org\tekuna\demo\weblog\model\Article;
	
	use org\tekuna\core\context\ContextObjectSupplied;
	use org\tekuna\core\context\ContextInitialized;
	
	use org\tekuna\plugin\pdodb\ConnectionManager;
	
	
	abstract class AbstractEntityManager implements ContextObjectSupplied, ContextInitialized {
		
		protected
			$objConnectionManager,
			$objDbConn;
		
		
		public function setPdoConnectionManager(ConnectionManager $objConnMgr) {
			
			$this -> objConnectionManager = $objConnMgr;
		}
		
		
		public function initialize() {
			
			$this -> objDbConn = $this -> objConnectionManager -> getDefaultConnection();
		}

		
		protected function fetchAllEntities(PDOStatement $objStatement) {
			
			$arrEntities = array();
			while ($objEntity = $objStatement -> fetchObject(static :: ENTITY_CLASS)) {
				
				$arrEntities[] = $objEntity;
			}
			
			return $arrEntities;
		}
		
		
		protected function fetchFirstEntity(PDOStatement $objStatement) {
			
			$objEntity = $objStatement -> fetchObject(static :: ENTITY_CLASS);
			
			if ($objEntity === false) {
				
				throw new PDOException("No entity found in Database.");
			}
			
			return $objEntity;
		}
		
		
		protected function getColumnNames() {
			
			$arrColumns = array();
			$sEntityClass = static :: ENTITY_CLASS;
			$objEntity = new $sEntityClass();
				
			foreach ($objEntity as $sName => $sValue) {
				
				$arrColumns[] = $sName;
			}
				
			return $arrColumns;
		}
		
		
		protected function buildStatement($sOtherStatements = '', $arrParameters = array()) {
			
			// prepare query parts
			$sColumns = implode(', ', $this -> getColumnNames());
			$sTable = static :: TABLE_NAME;
			
			// build query
			$sQuery = "select $sColumns from $sTable $sOtherStatements";
			
			// run query
			$objStatement = $this -> objDbConn -> prepare($sQuery);
			$objStatement -> execute($arrParameters);
			
			return $objStatement;
		}

	
		public function findAll() {

			return $this -> fetchAllEntities($this -> buildStatement());
		}
		
		
		public function findWhere($sColumn, $mValue) {
			
			$sWhereClause = "where $sColumn = :columnPlaceholder";
			$arrParameters = array(':columnPlaceholder' => $mValue);
			
			return $this -> fetchAllEntities($this -> buildStatement($sWhereClause, $arrParameters));
		}
		
		
		public function findFirstWhere($sColumn, $mValue) {
			
			$sWhereClause = "where $sColumn = :columnPlaceholder";
			$arrParameters = array(':columnPlaceholder' => $mValue);

			return $this -> fetchFirstEntity($this -> buildStatement($sWhereClause, $arrParameters));
		}
		
		
		public function save($objEntity) {
			
			if ($objEntity -> id > 0) {
				
				$this -> update($objEntity);
			}
			else {
				
				$this -> insert($objEntity);
			}
		}
		
		
		protected function insert($objEntity) {
			
			// build query parts
			$arrColumns = array();
			$arrPlaceholders = array();
			$arrValues = array();
			
			foreach ($objEntity as $sProperty => $sValue) {
				
				if ($sProperty == 'id') {
					
					continue;
				}
				
				$arrColumns[] = $sProperty;
				$sPlaceholder = ':col_'. md5($sProperty) .'x';
				$arrPlaceholders[] = $sPlaceholder;
				$arrValues[$sPlaceholder] = $sValue;
			}
			
			// build query
			$sQuery = 'insert into '. static :: TABLE_NAME .' ';
			$sQuery .= '('. implode(', ', $arrColumns) .') values';
			$sQuery .= '('. implode(', ', $arrPlaceholders) .');';
			
			// execute query
			$objStatement = $this -> objDbConn -> prepare($sQuery);
			$objStatement -> execute($arrValues);
		}
		
		
		protected function update($objEntity) {
			
			// build query parts
			$arrColumnPlaceholders = array();
			$arrValues = array();
			$iEntityId = 0;
			
			// build query and placeholders
			$sQuery = 'update '. static :: TABLE_NAME .' set ';
			foreach ($objEntity as $sProperty => $sValue) {
				
				// omit not set values
				if ($sValue === NULL) {
					
					continue;
				}
				
				// special handling for the ID
				if ($sProperty == 'id') {
					
					$arrValues[':id'] = $sValue;
					continue;
				}
				
				$sPlaceholder = ':col_'. md5($sProperty) .'x';
				$arrColumnPlaceholders[] = $sProperty .'='. $sPlaceholder;
				$arrValues[$sPlaceholder] = $sValue;
			}

			$sQuery .= implode(', ', $arrColumnPlaceholders);
			$sQuery .= ' where id = :id';
			
			// execute query
			$objStatement = $this -> objDbConn -> prepare($sQuery);
			$objStatement -> execute($arrValues);
		}
	}
